
#ifndef RTCONNECTORHTTPPROXYT_H
#define RTCONNECTORHTTPPROXYT_H

#include "RtReactorInterface.h"
#include "RtChannelHttpClient.h"
#include "RtHttpUrl.h"

template <class UpperType, class UpTrptType, class UpSockType>
class /*RT_API_EXPORT*/ CRtConnectorHttpProxyT 
	: public IRtConnectorInternal
	, public IRtChannelSink
{
public:
	CRtConnectorHttpProxyT(IRtReactor *aReactor, UpperType &aUpper)
		: m_pReactor(aReactor)
		, m_Upper(aUpper)
	{
	}
	
	virtual ~CRtConnectorHttpProxyT()
	{
		Close();
	}

	void SetProxyInfo(CRtHttpProxyInfo* aProxyInfo)
	{
		RT_ASSERTE_RETURN_VOID(aProxyInfo);

		m_pProxyInfo = aProxyInfo;
		RT_ASSERTE(
			m_pProxyInfo->GetProxyType() == CRtHttpProxyInfo::HTTP_PROXY || 
			m_pProxyInfo->GetProxyType() == CRtHttpProxyInfo::HTTPS_PROXY);
	}

	// interface IRtConnectorInternal
	virtual int Connect(const CRtInetAddr &aAddr, CRtInetAddr *aAddrLocal = NULL)
	{
		RT_ASSERTE_RETURN(m_pProxyInfo, -1);
		RT_ASSERTE(!m_HttpChannel);
		m_AddrServer = aAddr;

		CRtString strURL;
		strURL.reserve(128);
		if (aAddr.GetPort() == 443) {
			strURL = "https://";
			strURL += aAddr.GetIpDisplayName();
		}
		else {
			strURL = "http://";
			strURL += aAddr.GetIpDisplayName();
			char szPort[32];
			snprintf(szPort, sizeof(szPort), ":%d", static_cast<int>(aAddr.GetPort()));
			strURL += szPort;
		}
		CRtAutoPtr<CRtHttpUrl> pURL;
		RtResult rv = CRtChannelManager::Instance()->CreateUrl(
			pURL.ParaOut(),
			strURL);
		if (RT_FAILED(rv)) {
			RT_ERROR_TRACE_THIS("CRtConnectorHttpProxyT::Connect,"
				" unknown URL=" << strURL);
			return -1;
		}

		// don't call CRtChannelManager->CreateChannelHttpClient() 
		// in order to get CRtChannelHttpClient* directly.
		m_HttpChannel = new CRtChannelHttpClient(pURL.ParaIn(), m_pProxyInfo.ParaIn());
		RT_ASSERTE_RETURN(m_HttpChannel, -1);

		// SetRequestMethod CONNECT before calling AsyncOpen().
		rv = m_HttpChannel->SetRequestMethod("Connect");
		RT_ASSERTE(RT_SUCCEEDED(rv));

		rv = m_HttpChannel->AsyncOpen(this);
		return RT_SUCCEEDED(rv) ? 0 : -1;
	}
	
	virtual int Close()
	{
		if (m_HttpChannel) {
			m_HttpChannel->Disconnect(RT_OK);
			m_HttpChannel = NULL;
		}
		m_pProxyInfo = NULL;
		return 0;
	}

	virtual void OnConnect(RtResult aReason, IRtChannel *aChannelId)
	{
		RT_INFO_TRACE_THIS("CRtConnectorHttpProxyT::OnConnect, rv="<<aReason<<" channel="<<aChannelId);
		RT_ASSERTE(m_HttpChannel.Get() == aChannelId);
		if (RT_SUCCEEDED(aReason)) {
			CRtMessageBlock mbZero(0UL);
			aReason = m_HttpChannel->SendData(mbZero);
			RT_ASSERTE(RT_SUCCEEDED(aReason));
			if (RT_SUCCEEDED(aReason))
				return;
		}

		RT_ASSERTE(RT_FAILED(aReason));
		OnDisconnect(aReason, aChannelId);
	}

	virtual void OnReceive(
		CRtMessageBlock &aData, 
		IRtTransport *aTrptId, 
		CRtTransportParameter *aPara)
	{
		RT_ASSERTE(m_HttpChannel.Get() == aTrptId);

		LONG lState = 0;
		RtResult rv = m_HttpChannel->GetResponseStatus(lState);
		RT_ASSERTE(RT_SUCCEEDED(rv));

		RT_INFO_TRACE_THIS("CRtConnectorHttpProxyT::OnReceive,"
			" len=" << aData.GetChainedLength() <<
			" lState=" << lState);

		if (lState == 200) {
			CRtAutoPtr<IRtTransport> pTrans;
			m_HttpChannel->TransferTransport(pTrans.ParaOut());
			rv = m_HttpChannel->Disconnect(RT_OK);
			m_HttpChannel = NULL;
			RT_ASSERTE(pTrans);
			m_Upper.OnConnectIndication(RT_OK, pTrans.ParaIn(), this);
		}
		else {
			RT_WARNING_TRACE_THIS("CRtConnectorHttpProxyT::OnReceive, content: \n" << aData.FlattenChained());
			RtResult rvReason = RT_ERROR_FAILURE;
			if (lState >= 500 && lState < 600)
				rvReason = RT_ERROR_NETWORK_PROXY_SERVER_UNAVAILABLE;
			OnDisconnect(rvReason, aTrptId);
		}
	}

	virtual void OnSend(
		IRtTransport *aTrptId,
		CRtTransportParameter *aPara = NULL)
	{
		RT_ASSERTE(!"CRtConnectorHttpProxyT::OnSend, it shouldn't be called!");
	}

	virtual void OnDisconnect(
		RtResult aReason,
		IRtTransport *aTrptId)
	{
		RT_WARNING_TRACE_THIS("CRtConnectorHttpProxyT::OnDisconnect, rv="<<aReason<<" trpt="<<aTrptId);
		Close();
		if (RT_SUCCEEDED(aReason))
			aReason = RT_ERROR_NETWORK_SOCKET_ERROR;
		m_Upper.OnConnectIndication(aReason, NULL, this);
	}

private:
	IRtReactor *m_pReactor;
	UpperType &m_Upper;
	CRtInetAddr m_AddrServer;
	CRtAutoPtr<CRtChannelHttpClient> m_HttpChannel;
	CRtAutoPtr<CRtHttpProxyInfo> m_pProxyInfo;
};

#endif // !RTCONNECTORHTTPPROXYT_H
